﻿using System;
using System.Reflection;
using ManualSymbolStackTrace;
using Microsoft.Samples.Debugging.CorSymbolStore;
using PropertyExpression.Common;

//TODO: пересмотреть название наймспейсов

namespace PropertyExpression.ControllableQuery.Toolkit
{
    public abstract class QLocation
    {
        public class Class : QLocation
        {
            public readonly IQuery Query;

            public Class(IQuery query)
            {
                Query = query;
            }

            public override T Match<T>(Func<Class, T> @class, Func<Method, T> method, Func<NonDisplay, T> nonDisplay)
            {
                return @class(this);
            }
        }

        public class Method : QLocation
        {
            public readonly MethodInfo MethodInfo;

            public Method(MethodInfo methodInfo)
            {
                MethodInfo = methodInfo;
            }

            public override T Match<T>(Func<Class, T> @class, Func<Method, T> method, Func<NonDisplay, T> nonDisplay)
            {
                return method(this);
            }
        }

        public class NonDisplay : QLocation
        {
            public readonly MethodInfo MethodInfo;

            public NonDisplay(MethodInfo methodInfo)
            {
                MethodInfo = methodInfo;
            }

            public override T Match<T>(Func<Class, T> @class, Func<Method, T> method, Func<NonDisplay, T> nonDisplay)
            {
                return nonDisplay(this);
            }
        }

        public abstract T Match<T>(Func<Class, T> @class, Func<Method, T> method, Func<NonDisplay, T> nonDisplay);
    }

    public static class QLocationExtensions
    {
        public static string Display(this QLocation it)
        {
            return it.Match(
                @class => string.Format(@"
QueryType = '{0}'", @class.Query.GetType()),
                method => Display(method.MethodInfo),
                nonDisplay => Display(nonDisplay.MethodInfo));
        }

        private static string Display(MethodBase methodInfo)
        {
            return string.Format(@"
QueryLocation = '{0}'", GetQLocationString(methodInfo));
        }

        public static string GetQLocationString(MethodBase methodInfo)
        {
            IOption<SourceLocation> sourceLocation;
            try
            {
                sourceLocation = new StackTraceSymbolProvider(
                    methodInfo.ReflectedType.Assembly.Location,
                    SymSearchPolicies.AllowOriginalPathAccess |
                    SymSearchPolicies.AllowReferencePathAccess |
                    SymSearchPolicies.AllowRegistryAccess |
                    SymSearchPolicies.AllowSymbolServerAccess)
                    .GetSourceLocation(methodInfo);
            }
            catch
            {
                return DefaulttQLocationString(methodInfo);
            }
            return sourceLocation.Process(
                _ => string.Format("{0}:(Ln {1}, Col {2})",
                                   sourceLocation.Value.FilePath,
                                   sourceLocation.Value.Line,
                                   sourceLocation.Value.Column),
                () => DefaulttQLocationString(methodInfo));
        }

        private static string DefaulttQLocationString(MethodBase methodInfo)
        {
            return string.Format("{0}, {1}", methodInfo.DeclaringType, methodInfo.Name);
        }
    }
}